#include "copy.h"

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
long long total_bytes = 0;
_Atomic long long *completed_bytes;

// 复制文件 
void copy_file(const char *source_path, const char *dest_path) {
    FILE *source_file = fopen(source_path, "rb");
    if (source_file == NULL) {
        perror("fopen");
        return;
    }

    FILE *dest_file = fopen(dest_path, "wb");
    if (dest_file == NULL) {
        perror("fopen");
        fclose(source_file);
        return;
    }

    char buffer[BUFSIZ];
    size_t n;

    while ((n = fread(buffer, 1, sizeof(buffer), source_file)) > 0) {
        if (fwrite(buffer, 1, n, dest_file) != n) {
            perror("fwrite");
            fclose(source_file);
            fclose(dest_file);
            return;
        }

        atomic_fetch_add(completed_bytes, n);
    }

    fclose(source_file);
    fclose(dest_file);
}

// 以多线程方式复制文件
void *copy_file_threaded(void *arg) {
    struct ThreadData *data = (struct ThreadData *)arg;

    FILE *source_file = fopen(data->source_path, "rb");
    FILE *dest_file = fopen(data->dest_path, "wb");

    if (source_file == NULL || dest_file == NULL) {
        perror("File open error");
        return NULL;
    }

    fseek(source_file, data->start, SEEK_SET);
    fseek(dest_file, data->start, SEEK_SET);

    long long size = data->end - data->start;
    long long block_size = 1024 * 1024;  // 初始块大小，可以根据实际情况调整
    char *buffer = (char *)malloc(block_size);

    if (buffer == NULL) {
        perror("Memory allocation error");
        fclose(source_file);
        fclose(dest_file);
        return NULL;
    }

    long long bytes_processed = 0;
    size_t read_bytes, write_bytes;

    while (bytes_processed < size) {
        // 限制每次读取的字节数，以免超过文件剩余部分
        read_bytes = fread(buffer, 1, block_size, source_file);

        if (read_bytes == 0) {
            break;  // 读取完成或发生错误
        }

        write_bytes = fwrite(buffer, 1, read_bytes, dest_file);

        if (write_bytes != read_bytes) {
            perror("Write error");
            free(buffer);
            fclose(source_file);
            fclose(dest_file);
            return NULL;
        }

        // 使用原子操作更新已备份字节数
        atomic_fetch_add(completed_bytes, read_bytes);
        bytes_processed += read_bytes;
    }

    free(buffer);
    fclose(source_file);
    fclose(dest_file);

    pthread_exit(NULL);
}

// 递归复制整个目录
void copy_directory(const char *source, const char *destination) {
    DIR *dir;
    struct dirent *entry;
    struct stat stat_info;

    if ((dir = opendir(source)) == NULL) {
        perror("opendir");
        return;
    }

    if (access(destination, F_OK) == -1) {
        if (mkdir(destination, 0755) == -1) {
            perror("mkdir");
            return;
        }
    }

    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char source_path[PATH_MAX];
        char dest_path[PATH_MAX];

        snprintf(source_path, PATH_MAX, "%s/%s", source, entry->d_name);
        snprintf(dest_path, PATH_MAX, "%s/%s", destination, entry->d_name);

        if (lstat(source_path, &stat_info) == -1) {
            perror("lstat");
            continue;
        }

        if (S_ISDIR(stat_info.st_mode)) {
            copy_directory(source_path, dest_path);
        } else if (S_ISREG(stat_info.st_mode)) {
            if (stat_info.st_size > 500 * 1024 * 1024) {
            	//单个文件大小>500M时，开启多线程备份单文件功能
                copy_large_file_multithreaded(source_path, dest_path);
            } else {
                copy_file(source_path, dest_path);
            }
        }
    }

    closedir(dir);
}

// 复制大文件 
void copy_large_file_multithreaded(const char *source_path, const char *dest_path) {
    FILE *source_file = fopen(source_path, "rb");
    if (source_file == NULL) {
        perror("File open error");
        return;
    }

    fseek(source_file, 0, SEEK_END);
    long long file_size = ftell(source_file);
    fclose(source_file);

    pthread_t threads[NUM_THREADS];
    struct ThreadData data[NUM_THREADS];

    long long chunk_size = file_size / NUM_THREADS;
    for (int i = 0; i < NUM_THREADS; i++) {
        data[i].thread_id = i;
        data[i].start = i * chunk_size;
        data[i].end = (i == NUM_THREADS - 1) ? file_size : (i + 1) * chunk_size;
        strcpy(data[i].source_path, source_path);
        strcpy(data[i].dest_path, dest_path);

        pthread_create(&threads[i], NULL, copy_file_threaded, (void *)&data[i]);
    }

    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }
}

